package com.IntelligentCommunity.es.service.impl;

import com.IntelligentCommunity.common.core.domain.PageResult;
import com.IntelligentCommunity.common.core.domain.R;
import com.IntelligentCommunity.common.core.utils.StringUtils;
import com.IntelligentCommunity.es.domain.constants.EsConstants;
import com.IntelligentCommunity.es.domain.utils.SearchResultUtils;
import com.IntelligentCommunity.es.domain.vo.EsDocInsertVo;
import com.IntelligentCommunity.es.domain.vo.EsDocQueryVo;
import com.IntelligentCommunity.es.service.EsMatchingDataService;
import com.IntelligentCommunity.es.util.ESIndexUtil;
import lombok.extern.log4j.Log4j2;
import org.apache.lucene.search.TotalHits;
import org.elasticsearch.action.bulk.BulkItemResponse;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.elasticsearch.xcontent.XContentType;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import javax.validation.ConstraintViolation;
import javax.validation.Validator;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * @Author：作者姓名
 * @Package：com.IntelligentCommunity.es.service.impl
 * @Project：IntelligentCommunity
 * @name：EsMatchingDataServiceImpl
 * @Date：2024/7/18 23:50
 */
@Service
@Log4j2
public class EsMatchingDataServiceImpl implements EsMatchingDataService {

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    private final Validator validator;

    @Autowired
    private ESIndexUtil esIndexUtil;

    public EsMatchingDataServiceImpl(Validator validator, RestHighLevelClient restHighLevelClient) {
        this.validator = validator;
        this.restHighLevelClient = restHighLevelClient;
    }




    @Override
    public R<Boolean> insertCaptureData(List<EsDocInsertVo> esDocInsertVo) {

        EsDocInsertVo docInsertVo = esDocInsertVo.get(0);
        if(esDocInsertVo.get(0).getFlag()!=null){
            esIndexUtil.createIndex(docInsertVo.getIndexName(), docInsertVo.getFlag());
        }
        for (EsDocInsertVo vo : esDocInsertVo) {

            // 获取 EsDocInsertVo 中的 data
            Map<String, Object> data = vo.getData();

            // 验证 data 字段是否为空
            if (data == null || data.isEmpty()) {
                return R.fail(false, "数据校验失败：数据内容不能为空");
            }

            // 对 data 中的每个键值对进行验证
            for (Map.Entry<String, Object> entry : data.entrySet()) {
                String key = entry.getKey();
                Object value = entry.getValue();

                // 验证键不为空
                if (key == null || key.isEmpty()) {
                    return R.fail(false, "数据校验失败：键不能为空");
                }
                // 可以根据需要验证值的类型或其他约束条件
                // 这里示例只验证值不为空
                if (value == null) {
                    return R.fail(false, "数据校验失败：值不能为空");
                }
                // 使用 Validator 对值进行进一步验证
                Set<ConstraintViolation<Object>> violations = validator.validate(value);
                if (!violations.isEmpty()) {
                    StringBuilder errorMsg = new StringBuilder();
                    for (ConstraintViolation<Object> violation : violations) {
                        errorMsg.append(violation.getPropertyPath()).append(": ").append(violation.getMessage()).append("; ");
                    }
                    return R.fail(false, "数据校验失败：" + errorMsg.toString());
                }
            }
        }

        // 数据校验通过，执行插入操作
        BulkRequest request = new BulkRequest();
        esDocInsertVo.forEach(e -> {
            e.getData().put("createTime",new Date());
            IndexRequest indexRequest = new IndexRequest(e.getIndexName())
                    .source(e.getData(), XContentType.JSON);
            if (StringUtils.isNotEmpty(e.getId())) {
                indexRequest.id(e.getId());
            }
            request.add(indexRequest);
        });
        try {
            BulkResponse bulkResponse = restHighLevelClient.bulk(request, RequestOptions.DEFAULT);

            if (bulkResponse.hasFailures()) {
                // 处理批量请求中的失败情况
                for (BulkItemResponse bulkItemResponse : bulkResponse) {
                    if (bulkItemResponse.isFailed()) {
                        // 记录失败的文档信息和错误原因
                        log.error("插入失败: {}",
                                bulkItemResponse.getFailureMessage());
                    }
                }
                return R.fail(false, "部分文档插入失败");
            } else {
                return R.ok(true, "插入成功");
            }
        } catch (IOException e) {
            throw new RuntimeException();
        }
    }


    @Override
    public R<PageResult> selectCaptureData(EsDocQueryVo searchReq) {

        PageResult<Map> pageResult = new PageResult<>();

        if (StringUtils.isBlank(searchReq.getIndexName())){
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
            String format = sdf.format(date);
            if (searchReq.getFlag()!=null && searchReq.getFlag()==1) {
                searchReq.setIndexName("face" + format);
            }
            if (searchReq.getFlag()!=null && searchReq.getFlag()==2) {
                searchReq.setIndexName("car" + format);
            }
        }


        try {
            GetIndexRequest request = new GetIndexRequest(searchReq.getIndexName());
            boolean exists = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT);
            if (!exists) {
                return R.fail("数据不存在");
            }
        }catch (Exception e){
            throw new RuntimeException("获取索引错误");
        }

        SearchRequest searchRequest = new SearchRequest(searchReq.getIndexName());
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        List<String> fields = searchReq.getSearchFields();
        String[] fieldName = fields.toArray(new String[0]);

        // 条件查询
        if (searchReq.getQueryObj() != null) {
            searchReq.getQueryObj().forEach((k, v) -> {
                boolQueryBuilder.must(
                        QueryBuilders.termQuery(k, v)
                );
            });
        }

        if (null!=searchReq.getStartTime()){
            boolQueryBuilder.must(
                    QueryBuilders.rangeQuery(EsConstants.DATE_TIME)
                            .gte(searchReq.getStartTime().getTime())
            );
        }

        if (null!=searchReq.getEndTime()){
            boolQueryBuilder.must(
                    QueryBuilders.rangeQuery(EsConstants.DATE_TIME)
                            .lte(searchReq.getEndTime().getTime())
            );
        }

        if (StringUtils.isNotBlank(searchReq.getKeyWords())) {
            boolQueryBuilder.must(
                    QueryBuilders.multiMatchQuery(searchReq.getKeyWords(), fieldName)
            );
        }

        // 分页
        searchSourceBuilder.from((searchReq.getPageVo().getCurrentPage() - 1) * searchReq.getPageVo().getPageSize());
        searchSourceBuilder.size(searchReq.getPageVo().getPageSize());
        searchSourceBuilder.sort("createTime", SortOrder.DESC);
        searchSourceBuilder.query(boolQueryBuilder);

        // 高亮设置
        List<String> highLightFields = CollectionUtils.isEmpty(searchReq.getHighLightFields()) ?
                searchReq.getSearchFields() : searchReq.getHighLightFields();
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
        for (String high : highLightFields) {
            highlightBuilder.field(high);
        }
        searchSourceBuilder.highlighter(highlightBuilder);

        if(searchReq.getFlag() == 1) {
            searchSourceBuilder.sort(EsConstants.DATE_TIME, SortOrder.DESC);
        }else {
            searchSourceBuilder.sort(EsConstants.CAPTURE_TIME, SortOrder.DESC);
        }

        searchRequest.source(searchSourceBuilder);

        try {
            SearchResponse searchResponse = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            SearchHits searchResponseHits = searchResponse.getHits();
            TotalHits totalHits = searchResponseHits.getTotalHits();
            if (totalHits.value != 0) {
                SearchResultUtils.setTotal(totalHits.value);
            }

            List<Map> productInfoResponseList = new ArrayList<>();
            for (SearchHit searchHit : searchResponseHits.getHits()) {
                Map<String, Object> dataMap = searchHit.getSourceAsMap();
                    String id = searchHit.getId(); // 获取文档的_id字段值
                    dataMap.put("id", id);
                // 处理高亮
                Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
                if (highlightFields != null) {
                    for (String field : highLightFields) {
                        HighlightField highlightField = highlightFields.get(field);
                        if (highlightField != null) {
                            Text[] fragments = highlightField.getFragments();
                            StringBuilder sb = new StringBuilder();
                            for (Text fragment : fragments) {
                                sb.append(fragment);
                            }
                            dataMap.put(field, sb.toString());
                        }
                    }
                }

                productInfoResponseList.add(dataMap);
            }

            pageResult.setList(productInfoResponseList);
            pageResult.setTotal(totalHits.value);
            return R.ok(pageResult);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public R<Boolean> deleteFaceInfo(String id) {
        if (StringUtils.isBlank(id)){
            return R.fail(false, "id不能为空");
        }
            Date date = new Date();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyyMM");
            String format = sdf.format(date);
            String Index_Name="face" + format;
        try {
            // 构建Get请求
            GetRequest getRequest = new GetRequest(Index_Name, id);
            // 执行Get请求
            boolean exists = restHighLevelClient.exists(getRequest, RequestOptions.DEFAULT);

            if (exists) {
                // 如果文档存在，则执行删除操作
                DeleteRequest deleteRequest = new DeleteRequest("face202407", id);
                restHighLevelClient.delete(deleteRequest, RequestOptions.DEFAULT);
                return R.ok(null, "删除成功");
            } else {
                // 文档不存在的处理逻辑
                return R.fail("ID为" + id + "的文档不存在");
            }
        } catch (IOException e) {
            // 异常处理逻辑
            e.printStackTrace();
            return R.fail("删除失败");
        }
    }

    @Override
    public R<List<Map>> selectDoorAll(EsDocQueryVo esDocQueryVo) {
        List<Map> results = new ArrayList<>();
        try {
            if (StringUtils.isBlank(esDocQueryVo.getIndexName()))
            {
                throw new RuntimeException("索引名称不能为空");
            }
            // 创建搜索请求
            SearchRequest searchRequest = new SearchRequest();
            searchRequest.indices(esDocQueryVo.getIndexName());
            SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
            BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
            if (null != esDocQueryVo.getSearchFields() || esDocQueryVo.getSearchFields().size() > 0) {
                for (int i = 0; i < esDocQueryVo.getSearchFields().size(); i++) {
                    if (StringUtils.isNotBlank(esDocQueryVo.getSearchFields().get(i))) {
                        boolQueryBuilder.must(
//                                QueryBuilders.multiMatchQuery(esDocQueryVo.getKeyWordList().get(i), esDocQueryVo.getSearchFields().get(i))
                                QueryBuilders.matchQuery(esDocQueryVo.getSearchFields().get(i),esDocQueryVo.getKeyWordList().get(i))
                        );
                    }
                }
                searchSourceBuilder.query(boolQueryBuilder);
            }

            if (!(null != esDocQueryVo.getSearchFields() || esDocQueryVo.getSearchFields().size() > 0)) {
                // 构建查询
                searchSourceBuilder.query(QueryBuilders.matchAllQuery());
            }
            // 添加分页参数
            searchSourceBuilder.from((esDocQueryVo.getPageVo().getCurrentPage() - 1) * esDocQueryVo.getPageVo().getPageSize()).size(esDocQueryVo.getPageVo().getPageSize());

            searchRequest.source(searchSourceBuilder);

            // 执行搜索请求
            SearchResponse response = restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);

            // 处理搜索结果
            for (SearchHit hit : response.getHits().getHits()) {
                Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                results.add(sourceAsMap);
            }

        } catch (IOException e) {
            e.printStackTrace();
            return R.fail("查询失败: " + e.getMessage());
        }
        return R.ok(results);
    }
}
